#define MS_CLASS "Logger"
// #define MS_LOG_DEV_LEVEL 3

#include "Logger.hpp"
#include <uv.h>
#include <iostream>
#include <memory>
#include <stdarg.h>
#ifdef WIN32
#include <spdlog/sinks/wincolor_sink.h>
#else
#include <spdlog/sinks/ansicolor_sink.h>
#endif
#include <spdlog/sinks/rotating_file_sink.h>
#include "spdlog/spdlog.h"

using namespace std;
using namespace spdlog;
/* Class variables. */

const int64_t Logger::pid{ static_cast<int64_t>(uv_os_getpid()) };
Channel::UnixStreamSocket* Logger::channel{ nullptr };
char Logger::buffer[Logger::bufferSize];

/* Class methods. */

void Logger::ClassInit(Channel::UnixStreamSocket* channel)
{
	Logger::channel = channel;

	MS_TRACE();
}

static spdlog::level::level_enum toSpdlogLevel(LogLevel logLevel) {
    switch (logLevel) {
        case LogLevel::LOG_DEBUG:
            return spdlog::level::debug;
        case LogLevel::LOG_WARN:
            return spdlog::level::warn;
        case LogLevel::LOG_ERROR:
            return spdlog::level::err;
        case LogLevel::LOG_NONE:
            return spdlog::level::critical;
    }
}

static std::shared_ptr<spdlog::logger> _logger;

void Logger::ClassInit(const std::string &path, size_t maxFileSizeBytes, size_t maxFiles, LogLevel logLevel) {
    try {
        // Use a single logger with multiple sinks.
        // This means that the same log_msg is forwarded to multiple sinks;
        // Each sink can have it's own log level and a message will be logged.
        std::vector<spdlog::sink_ptr> sinks;
#if defined _WIN32 && !defined(__cplusplus_winrt)
        sinks.push_back(std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>());
#else
        sinks.push_back(std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>());
#endif
        sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(path, maxFileSizeBytes, maxFiles));

        _logger = make_shared<spdlog::logger>("webrtc-server", sinks.begin(), sinks.end());
        _logger->set_level(toSpdlogLevel(logLevel));

//        sinks[0]->set_level(spdlog::level::trace); // console. Allow everything.  Default value
//        sinks[1]->set_level(spdlog::level::trace); //  regular file. Allow everything.  Default value

        spdlog::register_logger(_logger);
        spdlog::set_pattern("[%Y-%m-%d %H:%M:%S:%e] [%t] [%^%l%$] %v");
    }
        // Exceptions will only be thrown upon failed logger or sink construction (not during logging)
    catch (const spdlog::spdlog_ex &ex) {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

void Logger::log(LogLevel level, const char *format, ...) {
    char buffer[10000];
    va_list ap;

    va_start(ap, format);
    vsprintf(buffer, format, ap);

    // Remove the artificial carriage return set by logging.
//    buffer[std::strlen(buffer) - 1] = '\0';
    switch (level) {
        case LogLevel::LOG_DEBUG:
            _logger->debug(buffer);
            break;
        case LogLevel::LOG_WARN:
            _logger->warn(buffer);
            break;
        case LogLevel::LOG_ERROR:
            _logger->error(buffer);
            break;
        case LogLevel::LOG_NONE:
            _logger->critical(buffer);
            break;
    }

    va_end(ap);
}
